using System;
using System.Globalization;
using LambdaGp.Core;

namespace LambdaGp.Operators.Terminals
{
    public class Ephemeral : Terminal
    {
        private readonly double _valueFrom;
        private readonly double _valueTo;
        private readonly double _decimalMultiplier;

        public Ephemeral(double valueFrom, double valueTo) :
            this(valueFrom, valueTo, 5)
        {
        }
        
        public Ephemeral(double valueFrom, double valueTo, int decimals) :
            base(string.Format("ephemeral({0}-{1})", valueFrom, valueTo))
        {
            _valueFrom = Math.Min(valueFrom, valueTo);
            _valueTo = Math.Max(valueFrom, valueTo);
            _decimalMultiplier = (int)Math.Pow(10, decimals);
        }

        public override bool RequiresParameterMutation { get { return true; } }

        public override Func<double> CompileToFunc(Individual individual, OperatorNode operatorNode)
        {
            throw new InvalidOperationException(
                "Ephemerals cannot be compiled, this should have been replaced by EphemeralInstance!");
        }

        public virtual EphemeralInstance CreateInstance(double value)
        {
            return new EphemeralInstance(this, value);
        }

        public override bool MatchesToken(string token, TokenPriority tokenPriority)
        {
            if (tokenPriority != TokenPriority.Secondary)
            {
                return false;
            }
            token = token.Replace(",", ".");
            double value;
            return
                double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
        }

        public override bool MatchesCompactToken(string token)
        {
            if (token.StartsWith("[") == false || token.EndsWith("]") == false)
            {
                return false;
            }

            token = token.Substring(1, token.Length - 2);
            token = token.Replace(",", ".");
            double value;
            return
                double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
        }

        public override Operator GetFromToken(string token)
        {
            if (token.StartsWith("[") || token.EndsWith("]"))
            {
                token = token.Substring(1, token.Length - 2);
            }

            base.GetFromToken(token);
            token = token.Replace(",", ".");
            double value = double.Parse(token, NumberStyles.Float, CultureInfo.InvariantCulture);
            return CreateInstance(value);
        }

        public override Operator ReplaceIfRequired()
        {
            double value = _valueFrom + (Population.Random.NextDouble() * (_valueTo - _valueFrom));
            value = ClampDecimals(value);
            return CreateInstance(value);
        }

        public double SlightlyMutateValue(double value)
        {
            double mut = ((_valueFrom - _valueTo) * ((2.0 * Population.Random.NextDouble()) - 1.0)) * 0.1;
            double newValue = ClampDecimals(value + mut);
            return newValue;
        }

        private double ClampDecimals(double value)
        {
            return Math.Round(_decimalMultiplier * value) / _decimalMultiplier;
        }
    }
}
